#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

__u8 *fb_base = NULL;
__u32 line_width;
__u32 pixel_width;

struct fb_var_screeninfo stVarInfo;



void lcd_put_pixel(int x, int y, __u32 color);

int main(int argc, char const *argv[])
{
    int fd_fb = open("/dev/fb0", O_RDWR);
    if (fd_fb < 0)
    {
        perror("open");
        return -1;
    }

       if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &stVarInfo))
    {
        perror("ioctl");
        return -2;
    }

    printf("xres:[%u], yres:[%u] bits_per_pixel:[%u]\n",
           stVarInfo.xres, stVarInfo.yres, stVarInfo.bits_per_pixel);

    struct fb_fix_screeninfo stfixInfo;
    if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &stfixInfo) < 0)
    {
        perror("ioctl");
        return -3;
    }

    printf("mem_start[0x%lx], mem_len[%d]\n", stfixInfo.smem_start, stfixInfo.smem_len);

    line_width = stVarInfo.xres * stVarInfo.bits_per_pixel / 8;
    pixel_width = stVarInfo.bits_per_pixel / 8;
    __u32 screen_size = stVarInfo.xres * stVarInfo.yres * stVarInfo.bits_per_pixel / 8; //screen_size 是整个 Framebuffer 的大小

    /*
    PROT_READ | PROT_WRITE 表示该区域可读、可写
    MAP_SHARED 表示该区域是共享的， APP 写入数据时，会直达驱动程序
    */
    fb_base = (unsigned char *)mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
    if (fb_base == MAP_FAILED)
    {
        perror("mmap");
        return -4;
    }
    int j = 0;
    while (++j < 10)
    {
        printf("[%d]\n",j);
        if(j % 2 == 1) 
        {
            memset(fb_base, 0xff, screen_size);//屏幕刷新为白色
        }
        else
        {
            memset(fb_base, 0x00, screen_size);//屏幕刷新为黑色
        }

        sleep(1);
    }
    


    munmap(fb_base, screen_size);
    close(fd_fb);
    return 0;
}

//使用中发现的段错误，未进一步定位
void lcd_put_pixel(int x, int y, __u32 color)
{
    __u8 *pen_8 = fb_base + y * line_width * x * pixel_width;
    __u16 *pen_16;
    __u32 *pen_32;
    __u32 red, green, blue;

    pen_16 = (__u16 *)pen_8;
    pen_32 = (__u32 *)pen_8;
    switch (stVarInfo.bits_per_pixel)
    {
        case 8:
        {
            *pen_8 = color;
            break;
        }
        case 16:
        {
            /* 565 */
            red = (color >> 16) & 0xff;
            green = (color >> 8) & 0xff;
            blue = (color >> 0) & 0xff;
            color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
            *pen_16 = color;
            break;
        }
        case 32:
        {
            *pen_32 = color;
            break;
        }
        default:
        {
            printf("can't surport %dbpp\n", stVarInfo.bits_per_pixel);
            break;
        }
    }
}

/*
第 28+36 行中传入的 color 表示颜色，它的格式永远是 0x00RRGGBB，即 RGB888。当 LCD 是 16bpp 时，要把 color 变量中的 R、 G、 B 抽出来再合并成 RGB565 格式。
第 30+36 行计算(x,y)坐标上像素对应的 Framebuffer 地址。
第 43+36 行，对于 8bpp， color 就不再表示 RBG 三原色了，这涉及调色板的概念， color 是调色板的值。
第 49～51+36 行，先从 color 变量中把 R、 G、 B 抽出来。
第 52+36 行，把 red、 green、 blue 这三种 8 位颜色值，根据 RGB565 的格式，只保留 red 中的高 5 位、green 中的高 6 位、 blue 中的高 5 位，组合成一个新的 16 位颜色值。
第 53+36 行，把新的 16 位颜色值写入 Framebuffer。
第 58+36 行，对于 32bpp，颜色格式跟 color 参数一致，可以直接写入 Framebuffer。
*/